
package simuri.similarity;

import java.sql.Connection;
import java.sql.Statement;
import java.util.Arrays;
import java.util.HashMap;
import java.util.TreeSet;
import java.util.regex.Pattern;
import org.apache.log4j.Logger;
import simuri.uridb.DbConnection;
import simuri.utils.Configuration;
import simuri.utils.EntityUriMap;
import simuri.utils.NamedEntity;
import simuri.utils.StopWords;
import uk.ac.shef.wit.simmetrics.similaritymetrics.QGramsDistance;

/**
 * Implements a similarity function based on QGrams. If resource are found that
 * match the input perfectly, then no further resources are retrieved
 * @author ngonga & fomy 
 */
public class DbTrigramsSimilarity implements DbSimilarityFunction {
    
  
   static Logger logger = Logger.getLogger("AEON");
   
    public HashMap<String, TreeSet<String>> getUriLabels(String endpoint,
            String graph, TreeSet<String> labelProperties) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

   // @Override
    public EntityUriMap getMappings(String inputText, TreeSet<NamedEntity> entities,
            HashMap<String, TreeSet<String>> uriLabels) {
        throw new UnsupportedOperationException("Not supported yet.");
    } 

   // @Override
    public EntityUriMap getMappings(String inputText, TreeSet<NamedEntity> entities) {
        
        QGramsDistance q = new QGramsDistance();
        EntityUriMap map = new EntityUriMap();
        for (NamedEntity e : entities) {
            HashMap<String, Double> uris = perfectGet(e.label);
            logger.info(e.label);
            logger.info("perfectGet matching:"+ uris);
            if (uris.isEmpty()) {
                uris = complexGet(e.label);
            logger.info("No perfect matches, Trying partial matches.:"+ uris);
            }
            map.put(e, uris);
            logger.info("show"+map.toString());
        }
        //result
        double sim;
        EntityUriMap result = new EntityUriMap();
        TreeSet<String> inputKeys = getStringSet(inputText);
        for (NamedEntity e : map.keySet()) {
            HashMap<String, Double> uris = new HashMap<>();
            
            for (String s : map.get(e).keySet()) {               
                sim = q.getSimilarity(mergeAllLabels(s).toString(), inputKeys.toString());
                logger.info( s + "\t" + sim);
                uris.put(s, sim);
            }
            result.put(e, uris);
        }
        return result;
    }
     
     /**
     * Check for perfectly matching URIs
     * @param res Label of entity
     * @return List of mapping URIs. Score is 1 (perfect match)
     */
      public HashMap<String, Double> perfectGet(String res) {

        HashMap<String, Double> result = new HashMap<>();
        String resource; 
        String uri = "http://dbpedia.org/resource/"+ res;
        Connection con = DbConnection.getConnection();
           
            try{
            Statement stmt = con.createStatement();
            String sql = "SELECT distinct uriname FROM words_uris WHERE uriname ='" + uri + "'";
            java.sql.ResultSet rset = stmt.executeQuery(sql);
            while (rset.next()) {
                resource = rset.getString("uriname");
                result.put(resource, 1.0);
            }
            stmt.close();
            con.close(); 
        } catch (Exception ex) {
            logger.warn(ex.getMessage());
        }
        return result;
    }
      
      
    /**
     * Check for URIs that contain entry as substring
     * @param res Label of entity
     * @return List of mapping URIs. Score is 1 (perfect match)
     */
    public HashMap<String, Double> complexGet(String res) {

        HashMap<String, Double> result = new HashMap<>();
        String resource;
         Connection con = DbConnection.getConnection();
            try{
            Statement stmt = con.createStatement();
            String sql = "SELECT distinct uriname FROM words_uris WHERE words = '" + res + "'";
            java.sql.ResultSet rset = stmt.executeQuery(sql);
            while (rset.next()) {
                resource = rset.getString("uriname");
                result.put(resource, 1.0);
            }
            stmt.close();
            con.close();
        } catch (Exception ex) {
            logger.warn(ex.getMessage());
        }
        return result;
    }

    /** Merges all labels of a given URI
     * @param uri URI whose labels are to be merged
     * @return All strings contained in the labels of the URI
     */   

   public TreeSet<String> mergeAllLabels(String uri) {
        TreeSet<String> strings = new TreeSet<>();
        TreeSet<String> labels;
        String label;
        Connection con = DbConnection.getConnection();

      try{      
        Statement stmt = con.createStatement();
        String sql = "SELECT words FROM words_uris WHERE uriname ='" + uri + "'";
        java.sql.ResultSet rset = stmt.executeQuery(sql);
        while (rset.next()) {
            label= rset.getString("words").toLowerCase();
            String split[] = label.toLowerCase().split(" ");
            strings.addAll(Arrays.asList(split));
        }
        
        stmt.close();
        con.close();
        } catch (Exception ex) {
            logger.warn(ex.getMessage());
        } 

        return strings;
    } 

    public TreeSet<String> getStringSet(String s) {
        TreeSet<String> result = new TreeSet<>();
        String split[] = s.split(" ");
        String token;
        for (int i = 0; i < split.length; i++) {
            token = process(split[i]);
            if (token.length() > 0) {
                result.add(token);
            }
        }
        return result;
    }

    /** Computes the Jaccard similarity of two sets of strings
     *
     * @param a First set of strings
     * @param b Second set of strings
     * @return Similarity value
     */
    public double jaccard(TreeSet<String> a, TreeSet<String> b) {

        if (a == null || b == null) {
            logger.fatal("Empty labels");
            return 0;
        }
        if (a.isEmpty() && b.isEmpty()) {
            return 0;
        }
        double intersection = 0.0;
        for (String s : a) {
            if (b.contains(s)) {
                intersection++;
            }
        }
        logger.info("Jaccard(" + a + ", " + b + ") = " + intersection / (double) (a.size() + b.size() - intersection));
        return intersection / (double) (a.size() + b.size() - intersection);
    }

    public String process(String s) {
        TreeSet<String> result = new TreeSet<>();
        StopWords sp = new StopWords();
        s = s.toLowerCase();
        s = s.replaceAll(Pattern.quote("("), "");
        s = s.replaceAll(Pattern.quote(")"), "");
        s = s.replaceAll(Pattern.quote("."), "");
        s = s.replaceAll(Pattern.quote("'s"), "");
        if (s.contains("@")) {
            s = s.substring(0, s.lastIndexOf("@"));
        }
        if (sp.isStopWord(s)) {
            return "";
        } else {
            return s;
            //return (new PorterStemmer()).stemWord(s);
        }
    }

    public static void main(String args[]) {
     // System.out.println(transformUri("http://dbpedia.org/class/yago/CentralProvince%28Kenya%29"));
       QGramsDistance q = new QGramsDistance();
       String in ="The Huffington Post reported Friday that U.S. presidential candidate Buddy Roemer received a $25 campaign donation from an individual listed as Homer Simpson.";
       DbTrigramsSimilarity dbt = new DbTrigramsSimilarity();
       System.out.println(dbt.mergeAllLabels("http://dbpedia.org/resource/the_huffington_post"));
       System.out.println(dbt.getStringSet(in));
       String s1 = dbt.mergeAllLabels("http://dbpedia.org/resource/Simpson_Peninsula").toString();
       String s2 = dbt.getStringSet(in).toString();
       long before = System.currentTimeMillis(); 
       double  sim = q.getSimilarity(s1, s2); 
    }
    
    public EntityUriMap getMappings(Configuration c, TreeSet<NamedEntity> entities) {
        return getMappings(c.inputText, entities);
      
    }

//    @Override
//    public EntityUriMap getMappings(String inputText, TreeSet<NamedEntity> entities, String sparqlEndpoint, String graph, TreeSet<String> labelProperties) {
//        throw new UnsupportedOperationException("Not supported yet.");
//    }

    
}
